package cas.ihep.spark;

import cas.ihep.pwa.api.IGlobalSet;
import cas.ihep.pwa.api.IRunningParam;
import cas.ihep.pwa.entity.PwaParameter;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class JobParser implements IGlobalSet,IRunningParam,Serializable{

    private ArrayList<String> m_equal=new ArrayList<>(),m_equar=new ArrayList<>();
    private ArrayList<List<String>> m_vmulpar=new ArrayList<>(),m_addMass=new ArrayList<>();
    private ArrayList<List<String>> m_addWidth=new ArrayList<>(),m_addReBw=new ArrayList<>(),m_addImBw=new ArrayList<>(),m_addSpecialBw=new ArrayList<>();
    private ArrayList<List<String>> m_addArgRe=new ArrayList<>(),m_addArgIm=new ArrayList<>(),m_addPhaseShift=new ArrayList<>();
    private ArrayList<List<String>> m_modeConstr=new ArrayList<>();
    private ArrayList<Double> m_parspinden=new ArrayList<>();
    private ArrayList<Integer> m_muldt=new ArrayList<>(),m_mulbg=new ArrayList<>(),m_mulmc=new ArrayList<>();
    private ArrayList<Double> m_vglb=new ArrayList<>(),m_vglbmax=new ArrayList<>();
    private ArrayList<PwaParameter> usrparnames=new ArrayList<>();
    private ArrayList<String> m_rootmcs=new ArrayList<>(),m_rootbgs=new ArrayList<>(),m_rootdts=new ArrayList<>();
    private ArrayList<Double> m_vimp=new ArrayList<>();

    private String m_pipiswavephase;
    transient String m_pdttable;
    transient String m_cvtcommand;
    transient String m_jobname;
    transient String m_decayname,m_userdecay;
    int fitstrategy,slices;
    private boolean xsection,dif;

    private double bgwt;

    public JobParser(){}

    public List<String> rootFileMcs(){
        return m_rootmcs;
    }

    public List<String> rootFileBgs(){
        return m_rootbgs;
    }

    public List<String> rootFileDts(){
        return m_rootdts;
    }

    private void parseStringList(LuaTable tb,List<String> lst){
        int count=tb.keyCount();
        for(int i=0;i<count;i++){
            LuaValue v=tb.get(i+1);
            if (v.isstring()){
                lst.add(v.tojstring());
            }
        }
    }
    private void parseStringList(LuaValue val, List<String> lst){
        if (val!=null && val.istable()){
            parseStringList((LuaTable)val,lst);
        }
    }

    private String parseString(LuaValue val){
        if (val!=null && val.isstring()){
            return val.tojstring();
        }
        throw new RuntimeException();
    }

    private void parseStringList2D(LuaValue val,List<List<String>> lst){
        if(val!=null && val.istable()){
            LuaTable tb1d=(LuaTable)val;
            int count1d=tb1d.keyCount();
            for(int i=0;i<count1d;i++){
                LuaValue val2d=tb1d.get(i+1);
                if (val2d.istable()){
                    ArrayList<String> tmp=new ArrayList<>();
                    parseStringList((LuaTable) val2d,tmp);
                    lst.add(tmp);
                }
            }
        }
    }

    private void parseDoubleList(LuaValue val,List<Double> lst){
        if(val!=null && val.istable()){
            LuaTable tb=(LuaTable)val;
            int count=tb.keyCount();
            for(int i=0;i<count;i++){
                LuaValue v=tb.get(i+1);
                if(v.isnumber()){
                    lst.add(v.todouble());
                }
            }
        }
    }

    private void parseIntList(LuaValue val,List<Integer> lst){
        if(val!=null && val.istable()){
            LuaTable tb=(LuaTable)val;
            int count=tb.keyCount();
            for(int i=0;i<count;i++){
                LuaValue v=tb.get(i+1);
                if(v.isnumber()){
                    lst.add(v.toint());
                }
            }
        }
    }

    private double parseDouble(LuaValue val){
        if(val.isnumber()){
            return val.todouble();
        }
        throw new RuntimeException();
    }

    private int parseInt(LuaValue val){
        if (val.isnumber()){
            return val.toint();
        }
        throw new RuntimeException();
    }

    private int parseInt(LuaValue val,int defval){
        if(val!=null && val.isnumber()){
            return val.toint();
        }
        return defval;
    }

    private boolean parseBoolean(LuaValue val){
        if(val.isboolean()){
            return val.toboolean();
        }
        throw new RuntimeException();
    }

    private double parseDouble(LuaValue val,double defval){
        if(val!=null && val.isnumber()){
            return val.todouble();
        }
        return defval;
    }

    private String parseString(LuaValue val,String defval){
        if (val!=null && val.isstring()){
            return val.tojstring();
        }
        return defval;
    }

    private boolean parseBoolean(LuaValue val,boolean defval){
        if(val!=null && val.isboolean()){
            return val.toboolean();
        }
        return defval;
    }

    public void parse(String ifile) throws IOException, ScriptException {
        String paralist=null;
        try(FileReader input=new FileReader(ifile)) {
            ScriptEngineManager sem = new ScriptEngineManager();
            ScriptEngine e = sem.getEngineByName("luaj");
            e.eval(input);
            parseStringList((LuaValue)e.get("ParEquaL"),m_equal);
            parseStringList((LuaValue)e.get("ParEquaR"),m_equar);
            m_pipiswavephase=(String)e.get("AddPipiSwavePhase");//parseString((LuaValue)e.get("AddPipiSwavePhase"),"");
            if (m_pipiswavephase==null){
                m_pipiswavephase="";
            }
            m_decayname=(String)e.get("DecayTable");
            m_userdecay=(String)e.get("UserDecayTable");
            parseStringList2D((LuaValue)e.get("MulPar"),m_vmulpar);
            parseStringList2D((LuaValue)e.get("AddMass"),m_addMass);
            parseStringList2D((LuaValue)e.get("AddWidth"),m_addWidth);
            parseStringList2D((LuaValue)e.get("AddReBW"),m_addReBw);
            parseStringList2D((LuaValue)e.get("AddImBW"),m_addImBw);
            parseStringList2D((LuaValue)e.get("AddSpecialBW"),m_addSpecialBw);
            parseStringList2D((LuaValue)e.get("AddArgRe"),m_addArgRe);
            parseStringList2D((LuaValue)e.get("AddArgIm"),m_addArgIm);
            parseStringList2D((LuaValue)e.get("AddPhaseShift"),m_addPhaseShift);
            parseDoubleList((LuaValue)e.get("ParSpinDensity"),m_parspinden);
            m_pdttable=(String)e.get("PdtTable");//parseString((LuaValue)e.get("PdtTable"));
            parseIntList((LuaValue)e.get("MulData"),m_muldt);
            parseIntList((LuaValue)e.get("MulBg"),m_mulbg);
            parseIntList((LuaValue)e.get("MulMc"),m_mulmc);
            m_vglb.add(1.0); m_vglb.add(1.0);
            m_vglbmax.add(1.0); m_vglbmax.add(1.0);
            paralist=(String)e.get("ParaList");//parseString((LuaValue)e.get("ParaList"));
            fitstrategy=parseInt((LuaValue)e.get("FitStrategy"),1);
            bgwt=parseDouble((LuaValue)e.get("BackgroundWeight"),1);
            xsection=parseBoolean((LuaValue)e.get("xsection"),false);
            parseStringList((LuaValue)e.get("RootFileMc"),m_rootmcs);
            parseStringList((LuaValue)e.get("RootFileBg"),m_rootbgs);
            parseStringList((LuaValue)e.get("RootFileDt"),m_rootdts);
            //m_decayname=parseString((LuaValue)e.get(""));
            parseStringList2D((LuaValue)e.get("ModeConstrain"),m_modeConstr);
            m_cvtcommand=(String)e.get("RootCvtCommand");//parseString((LuaValue)e.get("RootCvtCommand"));
            m_jobname=(String)e.get("Spark_JobName");//parseString((LuaValue)e.get("Spark_JobName"));
            slices=(int)e.get("Spark_Slices");//parseInt((LuaValue)e.get("Spark_Slices"));
            dif=parseBoolean((LuaValue)e.get("DepressInter"),false);
        }
        try(Scanner scanner=new Scanner(new File(paralist))){
            while(scanner.hasNext()){
                String xname=scanner.next();
                double value=scanner.nextDouble(),error=scanner.nextDouble(),low=scanner.nextDouble(),up=scanner.nextDouble();
                usrparnames.add(new PwaParameter(xname,value,error,low,up));
            }
        }
    }

    @Override
    public String pareql(int idx) {
        return m_equal.get(idx);
    }

    @Override
    public int pareqlSize() {
        return m_equal.size();
    }

    @Override
    public String pareqr(int idx) {
        return m_equar.get(idx);
    }

    @Override
    public int pareqrSize() {
        return m_equar.size();
    }

    @Override
    public List<Double> VGLB() {
        return m_vglb;
    }

    @Override
    public double[] basicVGLB() {
        int sz=m_vglb.size();
        double ret[]=new double[sz];
        for(int i=0;i<sz;i++){
            ret[i]=m_vglb.get(i);
        }
        return ret;
    }

    @Override
    public List<Double> VGLBmax() {
        return m_vglbmax;
    }

    @Override
    public double[] basicVGLBmax() {
        int sz=m_vglb.size();
        double ret[]=new double[sz];
        for(int i=0;i<sz;i++){
            ret[i]=m_vglb.get(i);
        }
        return ret;
    }

    @Override
    public List<Double> Vimp() {
        return m_vimp;
    }

    @Override
    public double[] basicVimp() {
        int sz=m_vimp.size();
        double ret[]=new double[sz];
        for(int i=0;i<sz;i++){
            ret[i]=m_vimp.get(i);
        }
        return ret;
    }

    @Override
    public List<Integer> mulMc() {
        return m_mulmc;
    }

    @Override
    public int[] basicMulMc() {
        int sz=m_mulmc.size();
        int ret[]=new int[sz];
        for(int i=0;i<sz;i++){
            ret[i]=m_mulmc.get(i);
        }
        return ret;
    }

    @Override
    public List<Integer> mulData() {
        return m_muldt;
    }

    @Override
    public int[] basicMulData() {
        int sz=m_muldt.size();
        int ret[]=new int[sz];
        for(int i=0;i<sz;i++){
            ret[i]=m_muldt.get(i);
        }
        return ret;
    }

    @Override
    public List<Integer> mulBg() {
        return m_mulbg;
    }

    @Override
    public int[] basicMulBg() {
        int sz=m_mulbg.size();
        int ret[]=new int[sz];
        for(int i=0;i<sz;i++){
            ret[i]=m_mulbg.get(i);
        }
        return ret;
    }

    @Override
    public String VMulPar(int i, int j) {
        return m_vmulpar.get(i).get(j);
    }

    @Override
    public int VMulParI() {
        return m_vmulpar.size();
    }

    @Override
    public String PipiSwavePhase() {
        return m_pipiswavephase;
    }

    @Override
    public double backgroundWeight() {
        return bgwt;
    }

    @Override
    public void setYesbw(boolean f) {
        _yesbw=f;
    }

    @Override
    public double parspinden(int idx) {
        return m_parspinden.get(idx);
    }

    private  boolean _yesbw=false;
    @Override
    public boolean yesbw() {
        return _yesbw;
    }

    @Override
    public String theparname(int i) {
        return usrparnames.get(i).parname;
    }

    @Override
    public int theparnameSize() {
        return usrparnames.size();
    }

    public PwaParameter parameter(int i){
        return usrparnames.get(i);
    }

    @Override
    public List<String> theparname() {
        ArrayList<String> ret=new ArrayList<>();
        for(PwaParameter param:usrparnames){
            ret.add(param.parname);
        }
        return ret;
    }

    @Override
    public String addMass(int i, int j) {
        return m_addMass.get(i).get(j);
    }

    @Override
    public int addMassSize() {
        return m_addMass.size();
    }

    @Override
    public String addWidth(int i, int j) {
        return m_addWidth.get(i).get(j);
    }

    @Override
    public int addWidthSize() {
        return m_addWidth.size();
    }

    @Override
    public String addArgRe(int i, int j) {
        return m_addArgRe.get(i).get(j);
    }

    @Override
    public int addArgReSize() {
        return m_addArgRe.size();
    }

    @Override
    public String addPhaseShift(int i, int j) {
        return m_addPhaseShift.get(i).get(j);
    }

    @Override
    public int addPhaseShiftSize() {
        return m_addPhaseShift.size();
    }

    @Override
    public List<String> addPhaseShift(int i) {
        return m_addPhaseShift.get(i);
    }

    @Override
    public String addReBw(int i, int j) {
        return m_addReBw.get(i).get(j);
    }

    @Override
    public int addReBwSize() {
        return m_addReBw.size();
    }

    @Override
    public String addImBw(int i, int j) {
        return m_addImBw.get(i).get(j);
    }

    @Override
    public int addImBwSize() {
        return m_addImBw.size();
    }

    @Override
    public String addSpecialBW(int i, int j) {
        return m_addSpecialBw.get(i).get(j);
    }

    @Override
    public List<String> addSpecialBW(int i) {
        return m_addSpecialBw.get(i);
    }

    @Override
    public int addSpecialBWSize() {
        return m_addSpecialBw.size();
    }

    @Override
    public String addArgIm(int i, int j) {
        return m_addArgIm.get(i).get(j);
    }

    @Override
    public int addArgImSize() {
        return m_addArgIm.size();
    }

    @Override
    public List<String> addArgRe(int i) {
        return m_addArgRe.get(i);
    }

    public List<String> modeConstrain(int i){
        return m_modeConstr.get(i);
    }

    public int modeConstrainSize(){
        return m_modeConstr.size();
    }

    public String modeConstrain(int i,int j){
        return m_modeConstr.get(i).get(j);
    }

    public boolean depressInter(){
        return dif;
    }
}
